package com.smsc.headend.task.engine.handler.impl.firmware;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.smsc.headend.common.enums.errorcode.TaskExecuteErrorCode;
import com.smsc.headend.common.enums.errorcode.TaskHandlerErrorCode;
import com.smsc.headend.common.utils.RedisKeys;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.data.dto.TaskDataDTO;
import com.smsc.headend.module.data.enums.OperationName;
import com.smsc.headend.module.fwu.entity.TemplateFwUpgrade;
import com.smsc.headend.module.protocol.DeviceData;
import com.smsc.headend.module.protocol.cosem.bo.CosemData;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskHandlerException;
import com.smsc.headend.task.engine.handler.TaskHandler;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.task.engine.service.CosemXmlService;
import com.smsc.headend.task.engine.service.firmware.FirmwareService;
import com.smsc.headend.task.engine.utils.TaskParamUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * @author liangli
 * @date 2020/9/25
 */
@Slf4j
public class ReadFirstNotTransferredBlockHandler implements TaskHandler {
    @Autowired
    TaskDAO taskDAO;
    @Autowired
    RedisUtils redisUtils;
    @Autowired
    CosemXmlService cosemXmlService;
    @Autowired
    FirmwareService firmwareService;
    @Value("#{T(java.lang.Integer).parseInt('${fwu.step.retryTimes:2}')}")
    Integer firmwareStepsRetry;
    TemplateFwUpgrade.TaskType taskType = TemplateFwUpgrade.TaskType.ImageTransfer;

    @Override
    public int handle(Task task, AtomicTask atomicTask, DeviceData deviceData, String actionResult) throws UDISTaskHandlerException {
        if (!(deviceData instanceof CosemData)) {
            throw new UDISTaskHandlerException(TaskHandlerErrorCode.DEVICE_DATA_TYPE_NOT_SUPPORT.toString());
        }

        CosemData cosemData = (CosemData) deviceData;
        Map<String, Object> paramMap = this.getParameterMap(task, atomicTask);
        Long fwFileId = Convert.toLong(paramMap.get("fwFileId"));
        List<TaskDataDTO> dataDTOS = taskDAO.getAllTaskData(task.getTaskNo());
        TaskDataDTO dataDTO = dataDTOS.stream().filter(taskDataDTO -> taskDataDTO.getKey().equalsIgnoreCase("blockSize")).findAny().get();
        Integer blockSize = Integer.valueOf(String.valueOf(dataDTO.getValue()));

        String fwCacheKey = RedisKeys.getTaskFirmwareFile(fwFileId, blockSize);
        Set<String> keys = redisUtils.hKeys(fwCacheKey);
        Integer totalBlockNumber = keys.size();
        Integer lastNotTransferredBlockNumber = Convert.toInt(cosemXmlService.getData(cosemData.getType(), cosemData.getValue()));
        if (lastNotTransferredBlockNumber < totalBlockNumber) {
            log.info("task {} block {} not transferred", task.getTaskNo(), lastNotTransferredBlockNumber);
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), "resendStatus", 1, 6000L, true);
            AtomicTask transferTask = task.getSubTaskGroups().stream().flatMap(subTaskGroup -> subTaskGroup.getSubTasks().stream()).filter(aTask ->
                    aTask.getOperationId().intValue() == OperationName.ImageBlockTransfer.getId()
            ).findFirst().orElse(null);
            AtomicTask resendBlockAtomicTask = new AtomicTask();
            BeanUtils.copyProperties(transferTask, resendBlockAtomicTask, "atomicTaskNo");
            resendBlockAtomicTask.setAtomicTaskNo(UUID.randomUUID().toString());
            taskDAO.atomicTaskQueueAddReverse(resendBlockAtomicTask);

            AtomicTaskState atomicTaskState = new AtomicTaskState();
            atomicTaskState.setTaskNo(task.getTaskNo());
            atomicTaskState.setAtomicTaskNo(resendBlockAtomicTask.getAtomicTaskNo());
            atomicTaskState.setBlockNumber(lastNotTransferredBlockNumber);
            taskDAO.hashSetAtomicTaskState(resendBlockAtomicTask.getAtomicTaskNo(), task, 0, atomicTaskState);
            return 0;
        }
        else {

        }
        return 1;
    }

    @Override
    public int handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.error("read not transferred block error {}, ", message);
        if (firmwareService.isRetryMessageType(message)) {
            AtomicTaskState atomicTaskState = taskDAO.hashGetAtomicTaskState(atomicTask.getAtomicTaskNo());
            if (atomicTaskState.getRetryTimes() >= firmwareStepsRetry) {
                log.error("Image transfer failed retry times over");
                firmwareService.saveFwuReport(task, atomicTask, -1, taskType, StrUtil.format("not transferred failed,{}", message), null);
                return -1;
            }
            log.error("Image transfer failed retry times {}", atomicTaskState.getRetryTimes());
            atomicTaskState.setStored(false);
            taskDAO.hashSetAtomicTaskState(atomicTask.getAtomicTaskNo(), task, atomicTaskState.getRetryTimes() + 1,  atomicTaskState);
            return 0;
        }

        Map<String, Object> param = TaskParamUtils.getParameterMap(task, atomicTask);
        log.error("read not transferred block  failed: taskNo={}, param={}", task.getTaskNo(), JSONUtil.toJsonStr(param));
        firmwareService.saveFwuReport(task, atomicTask, -1, taskType, null, null);
        return -1;
    }
}
